package com.csaba.connector.citi.model;

import java.io.FileOutputStream;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.httpclient.Cookie;
import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.params.HttpMethodParams;

import com.csaba.connector.ServiceException;
import com.csaba.connector.citi.parser.CitiCodes;
import com.csaba.connector.model.Session;
import com.csaba.util.LoggerHelper;

public class CitiSession extends Session
{
	private final static Logger logger = LoggerHelper.getDefaultLogger();

	public final static boolean DEBUG_FILE = true; //FIXME set to false
	public final static boolean DEBUG_COOKIE = true; //FIXME set to false	

	private HttpClient client;

	private int operationCounter = 1;

	public CitiSession()
	{
		super();
		setBank(CitiBank.getInstance());
	}

	public int getOperationCounter()
	{
		return operationCounter++;
	}

	public String doPost( final String url, final NameValuePair[] params ) throws ServiceException
	{
		final PostMethod method = new PostMethod(url);
		method.addParameters(params);
		method.setRequestHeader("User-Agent", CitiRequestFactory.MOZILLA);
		return execute(method);
	}

	public String doGet( final String url ) throws ServiceException
	{
		final GetMethod method = new GetMethod(url);
		method.setRequestHeader("User-Agent", CitiRequestFactory.MOZILLA);
		return execute(method);
	}

	/**
	 * Create a TimeStamp at end of URL. 
	 * Where it is needed, it is signed with TS_NEEDED text in name of URL string.
	 * @return with the current time (String) e.g.: "1222431063308"
	 */
	private String getTimeStamp()
	{
		final Date date = new Date();
		final Long time = date.getTime();
		return time.toString();
	}

	public String doGetWithTimeStamp( final String url ) throws ServiceException
	{
		return doGet(url + getTimeStamp());
	}

	private String execute( final HttpMethod method ) throws ServiceException
	{
		// lazy initialization
		if ( client == null )
		{
			client = new HttpClient();
		}

		//Provide custom retry handler is necessary
		method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler(3, false));

		try
		{
			final int statusCode = client.executeMethod(method);
			loggerCookies();

			if ( statusCode == HttpStatus.SC_OK )
			{
				return method.getResponseBodyAsString();
			}
			else if ( statusCode == HttpStatus.SC_MOVED_TEMPORARILY || statusCode == HttpStatus.SC_MOVED_PERMANENTLY )
			{
				final Header locationHeader = method.getResponseHeader("location");
				if ( locationHeader != null )
				{
					String redirectLocation = locationHeader.getValue();
					if ( !redirectLocation.startsWith(CitiRequestFactory.HOMEBANK_URL_BASE)
							&& redirectLocation.startsWith("/") )
					{
						redirectLocation = CitiRequestFactory.HOMEBANK_URL_BASE + redirectLocation;
					}
					logger.fine("Being redirected to " + redirectLocation);
					return doGet(redirectLocation);

				}
				else
				{
					throw new ServiceException("Citi: Invalid redirect.", "Missing location.");
				}
			}
			else
			{
				throw new ServiceException("Citi: Unexpected HTTP response:" + statusCode, method.getStatusLine()
						.toString());
			}

		}
		catch ( final Exception e )
		{
			throw new ServiceException("Unexpected exception in HTTP communication.", e);
		}
		finally
		{
			// Release the connection.
			method.releaseConnection();
		}
	}

	public void debugFile( final String fileName, final String content )
	{
		logger.fine(fileName + " result is received. Length: " + content.length());
		if ( CitiSession.DEBUG_FILE )
		{
			try
			{
				final FileOutputStream fos = new FileOutputStream("../citi.source/" + fileName + ".html");

				fos.write(content.getBytes("UTF8"));
				fos.flush();
				fos.close();
			}
			catch ( final Exception ex )
			{
				logger.log(Level.SEVERE, "Error creating debug file.", ex);
			}
		}

	}

	public final NameValuePair getCookieValue( final String cookie )
	{
		final Cookie[] cookies = client.getState().getCookies();
		NameValuePair nameValuePair = null;
		if ( cookies != null )
		{
			int i = 0;
			while ( nameValuePair == null && i < cookies.length )
			{
				if ( cookie.equals(cookies[i].getName()) )
					nameValuePair = cookies[i];
				i++;
			}
		}
		return nameValuePair;
	}

	public final boolean isSessionIDAvailable()
	{
		return getCookieValue(CitiCodes.JSESSIONID) != null;
	}

	private void loggerCookies()
	{
		if ( CitiSession.DEBUG_COOKIE )
		{
			// Get all the cookies
			final Cookie[] cookies = client.getState().getCookies();
			// Display the cookies
			logger.fine("Present cookies: ");
			for ( int i = 0; i < cookies.length; i++ )
			{
				logger.fine(" - " + cookies[i].toExternalForm());
			}
		}
	}

}
